package com.codeyang.aspect;

import com.alibaba.fastjson.JSON;
import com.codeyang.anno.Log;
import com.codeyang.config.ManagerThreadPool;
import com.codeyang.domain.SysLog;
import com.codeyang.domain.SysUser;
import com.codeyang.service.SysLogService;
import com.codeyang.service.SysUserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 描述: 日志切面处理
 *
 * @author CodeYang_Site
 * @version 2021/5/24 15:17
 */
@Aspect
@Component
// proxyTargetClass时 就是指定必须使用CGlib来创建代理对象
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class LogAspect {

    /**
     * lazy 保证懒加载,保证 一定完成了 bean的注入
     */
    @Autowired
    @Lazy
    private SysUserService sysUserService;

    @Autowired
    @Lazy
    private SysLogService sysLogService;


    /**
     * 指定扫描 带有@Log注解的方法做切面增强
     * 记录指定方法操作插入DB -mysql,redis,mongodb,es
     * 这里插入mysql
     *
     * @param proceedingJoinPoint
     * @return
     */
    @Around(value = "@annotation(com.codeyang.anno.Log)")
    public Object handleLogAspect(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;
        //1.拿到注解->拿到方法
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        //拿到注解
        Log annotation = method.getAnnotation(Log.class);
        //拿到注解上属性值
        String operation = annotation.operation();
        //2.拿到方法名称
        String methodName = method.getName();
        //3.拿到当前用户
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        //需要用户名 从DB中查一个出来
        SysUser sysUser = sysUserService.getById(userId);
        //4.拿到ip地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = request.getRemoteHost();
        //拿到执行时长
        long start = System.currentTimeMillis();
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        long end = System.currentTimeMillis();
        //写DB是比较耗IO的,直接交给异步线程池去操作
        // 线程池里的参数 不受外界的影响 相当于是一个 函数方法|copy了一份
        ManagerThreadPool.poolExecutor.execute(() -> {
            Object[] args = proceedingJoinPoint.getArgs();
            //组装SysLog
            SysLog sysLog = SysLog.builder()
                    .method(methodName)
                    .ip(ip)
                    .operation(operation)
                    .username(sysUser.getUsername())
                    .createDate(new Date())
                    .params(args == null ? "" : JSON.toJSONString(args))
                    .time(end-start)
                    .build();
            sysLogService.save(sysLog);
        });
        return result;
    }
}
